/**
 * 
 */
package com.worksmap.common;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;

import com.worksmap.core.constant.TableConstant;
import com.worksmap.core.entity.User;
import com.worksmap.core.util.DaoException;
import com.worksmap.core.util.StringUtil;


/**
 * dao的基类
 * 所有dao都得继承该类,目前还未应用，先做测试
 * 1. 所有的sql语句都得遵循以下格式：
 * insert into user(username,password,first_name,last_name,birthday,age) 
 * 					values(:userName,:passWord,:firstName,:lastName,:birthday,:age)：
 * 其中“:name”就是命名参数；
 * @author jack
 * 2013.9.4
 */
public abstract class GeneralDao {
	
	/**
	 * log4j日志记录器
	 */
	protected Logger logger = Logger.getLogger(this.getClass());
	
	//2013.7.22 换一种注入方式
	/**
	 * 在讲解依赖注入的3种实现方式之前，这里先澄清一下依赖注入的意义：
	 * 让组件依赖于抽象，当组件要与其他实际对象发生依赖关系时，通过抽象来注入依赖的实际对象。 
	 * 依赖注入的3种实现方式分别是：接口注入（interface injection）、Set注入（setter injection）和构造注入（constructor injection）。
	 */
	@Autowired
	private SimpleJdbcTemplate simpleJdbcTemplate;
	
//	private SimpleJdbcTemplate simpleJdbcTemplate;
//	
//	/**
//	 * @param simpleJdbcTemplate the simpleJdbcTemplate to set
//	 */
//	@Resource(name="dataSource")
//	public void setSimpleJdbcTemplate(DataSource dataSource) {
//		this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
//	}
	
	/**
	 * 通过sql查询对象list
	 * @param sql 数据库select语句格式：select * from user where userName like ? and passWord = ? 
	 * @param values 对象数组，sql语句有几个？，则数组有几个对象，一一对应: new Object[]{"%wang%", "admins"}
	 * jack
	 * 2013-9-5
	 */
	public <T> List<T> getEntityList(Class<T> beanClass, String sql, Object[] values) throws Exception{
		List<T> beanList = new ArrayList<T>();
		try{
			beanList = simpleJdbcTemplate.query(sql.toString(), BeanPropertyRowMapper.newInstance(beanClass), values);
		}catch (DataAccessException e){
			logger.error("通过sql查询对象list出错！" + e);
		}
		return beanList;
	}
	
	/**
	 * 通过sql查询对象
	 * @param sql 数据库select语句格式：select * from user where userName = ? 
	 * @param values 对象数组，sql语句有几个？，则数组有几个对象，一一对应: new Object[]{"wang"}
	 * jack
	 * 2013-9-11
	 */
	public <T> T getEntity(Class<T> beanClass, String sql, Object[] values) throws Exception{
		T bean = null;
		try{
			bean = simpleJdbcTemplate.queryForObject(sql.toString(), BeanPropertyRowMapper.newInstance(beanClass), values);
		}catch(EmptyResultDataAccessException e){
			return null;
		}catch(DataAccessException e){
			logger.error("通过sql查询对象出错！" + e);
		}
		return bean;
	}
	
	/**
	 * 通过主键值查出对象
	 * @param beanClass 
	 * @param primaryKeyValue 主键的值，根据该值查出对象数据
	 */
	public <T> T getEntity(Class<T> beanClass, Object primaryKeyValue) throws Exception{
		T bean = null;
		String tableName = TableConstant.TABLE_BEAN.get(beanClass.getName());
		String primaryKey = TableConstant.TABLE_PRIMARY_KEY.get(tableName);
		if(StringUtil.isEmpty(tableName)){
			logger.info("对象实体bean未与数据库表名称映射，请维护TableConstant.java");
			return null;
		}
		if(StringUtil.isEmpty(primaryKey)){
			logger.info("数据库表名称未与表主键字段映射，请维护TableConstant.java");
			return null;
		}
		StringBuilder sql = new StringBuilder(" select * from ");
		sql.append(tableName).append(" where ").append(primaryKey).append(" = ? ");
		try{
			bean = simpleJdbcTemplate.queryForObject(sql.toString(), BeanPropertyRowMapper.newInstance(beanClass), primaryKeyValue);
		}catch(EmptyResultDataAccessException e){
			logger.info("没有查出结果,Incorrect result size: expected 1, actual 0");
			return null;
		}catch (DataAccessException e){
			logger.error("通过主键值查出对象出错！" + e);
		}
		return bean;
	}
	
	/**
	 * 获取Map集合值
	 * @param sql
	 * @param obj
	 * @return
	 */
	public Map<String,?> getMap(String sql,Object[] obj){
		
		Map<String, ?> map = null;
		try {
			map = simpleJdbcTemplate.queryForMap(sql, obj);
		} catch (Exception e) {
			logger.info("获取Map集合值失败！" + e);
			throw new DaoException("数据库操作失败！",e);
		}
		
		return map;
	}
	
	/**
	 * 删除对象
	 * @param entity 对象中主键必须有值
	 * jack
	 * 2013-9-5
	 */
	public boolean delEntity(Object entity) throws Exception{
		boolean delFlag = true;
		String tableName = TableConstant.TABLE_BEAN.get(entity.getClass().getName());
		String primaryKey = TableConstant.TABLE_PRIMARY_KEY.get(tableName);
		if(StringUtil.isEmpty(tableName)){
			logger.info("对象实体bean未与数据库表名称映射，请维护TableConstant.java");
			return false;
		}
		if(StringUtil.isEmpty(primaryKey)){
			logger.info("数据库表名称未与表主键字段映射，请维护TableConstant.java");
			return false;
		}
		StringBuilder sql = new StringBuilder(" delete from ");
		sql.append(tableName).append(" where ");
		sql.append(primaryKey).append(" = ? ");
		List<Object> objList = new ArrayList<Object>();
		PropertyDescriptor pd = new PropertyDescriptor(primaryKey, entity.getClass());   
        Method getMethod = pd.getReadMethod();  //获得get方法   
        Object primaryKeyValue = getMethod.invoke(entity);
		objList.add(primaryKeyValue);
		try{
			int returnFlag = simpleJdbcTemplate.update(sql.toString(), objList.toArray());
			logger.info("returnFlag打印输出：" + returnFlag);
		}catch(DataAccessException e){
			logger.error("删除对象出错！" + e);
			delFlag = false;
		}
		return delFlag;
	}
	
	/**
	 * 新增数据库数据
	 * 1.sql语句格式：insert into user(username,password,first_name,last_name,birthday,age) 
	 * 					values(:userName,:passWord,:firstName,:lastName,:birthday,:age)
	 * @param sql 
	 * @param obj 实体对象
	 * @return
	 */
	public boolean saveEntity(String sql, Object obj) throws Exception {
		return addOrUpdate(sql, obj);
	}

	/**
	 * 通过sql更新数据库
	 * @param sql 数据库update语句格式：update stu set s_name=?, s_sex=?, s_brith=? where id=?
	 * @param obj 实体对象
	 * @return
	 */
	public boolean updateEntity(String sql, Object obj) throws Exception{
		return addOrUpdate(sql, obj);
	}
	
	/**
	 * 通过sql更新数据库
	 * @param sql 数据库update语句格式：update stu set s_name=?, s_sex=?, s_brith=? where id=?
	 * @param values 对象数组，update的sql语句有几个？，则数组有几个对象，一一对应
	 * jack
	 * 2013-9-5
	 */
	public boolean updateEntity(String sql, Object[] values) throws Exception{
		boolean updateFlag = true;
		try{
			int returnFlag = simpleJdbcTemplate.update(sql, values);
			logger.info("returnFlag打印输出：" + returnFlag);
		}catch(DataAccessException e){
			logger.error("通过sql更新数据库出错！" + e);
			updateFlag = false;
		}
		return updateFlag;
	}
	
	/**
	 * 更新一个对象的指定字段
	 * @param entity 要更新的对象，主键必须有值
	 * @param updateFields String的数组，更新对象的哪些字段
	 * jack
	 * 2013-9-5
	 */
	public boolean updateEntity(Object entity, String... updateFields) throws Exception {
		Class<?> clazz = entity.getClass();   
		String tableName = TableConstant.TABLE_BEAN.get(clazz.getName());
		String primaryKey = TableConstant.TABLE_PRIMARY_KEY.get(tableName);
		if(StringUtil.isEmpty(tableName)){
			logger.error("对象实体bean未与数据库表名称映射，请维护TableConstant.java");
			throw new Exception("对象实体bean未与数据库表名称映射，请维护TableConstant.java");
		}
		if(StringUtil.isEmpty(primaryKey)){
			logger.error("数据库表名称未与表主键字段映射，请维护TableConstant.java");
			throw new Exception("数据库表名称未与表主键字段映射，请维护TableConstant.java");
		}
		return updateEntityString(clazz, tableName, primaryKey, entity, updateFields);
    }
	
	private boolean updateEntityString (Class<?> clazz, String tableName, String primaryKey, Object entity, String ... updateFields) throws Exception{
		boolean updateFlag = true;
		StringBuilder sqlBuilder = new StringBuilder("update ").append(tableName);
		sqlBuilder.append(" set ");
		for(int i=0; i<updateFields.length; i++){
			sqlBuilder.append(updateFields[i] + " = ? ").append(i == updateFields.length - 1 ? "" : ", ");
		}
//		simpleJdbcTemplate.update("update stu set s_name=?,s_sex=?,s_brith=? where s_id=?", "a", "b" ,"c", "d");
		List<Object> objList = new ArrayList<Object>();
		for(String field:updateFields){
			//需要利用反射机制，将对象的值取出放到objList中
		    PropertyDescriptor pd = new PropertyDescriptor(field, clazz);   
	        Method getMethod = pd.getReadMethod();  //获得get方法   
			objList.add(getMethod.invoke(entity));
		}
		sqlBuilder.append(" where ");
		sqlBuilder.append(primaryKey).append(" = ? ");   //获得表的主键字段名称
		PropertyDescriptor pd = new PropertyDescriptor(primaryKey, clazz);   
        Method getMethod = pd.getReadMethod();  //获得get方法   
		objList.add(getMethod.invoke(entity));
		try{
			int returnFlag = simpleJdbcTemplate.update(sqlBuilder.toString(), objList.toArray());
			logger.info("returnFlag打印输出：" + returnFlag);
		}catch(DataAccessException e){
			logger.error("更新一个对象的指定字段出错！" + e);
			updateFlag = false;
		}
        return updateFlag;
	}
	
	private boolean addOrUpdate(String sql,Object obj){
		boolean updateFlag = true;
		try {
			SqlParameterSource paramSource = new BeanPropertySqlParameterSource(obj);  
			int returnFlag = simpleJdbcTemplate.update(sql, paramSource);
			logger.info("returnFlag打印输出：" + returnFlag);
		} catch (Exception e) {
			logger.error("数据库操作失败！" + e);
			throw new DaoException("数据库操作失败！",e);
		}
		return updateFlag;
	}
	
	/**
	 * 测试
	 * jack
	 * 2013-9-5
	 */
	public List<User> test() throws Exception{
		List<User> userList = null;
		userList = getEntityList(User.class, "select * from user where userName like ? and passWord = ? ", new Object[]{"%wang%", "admins"});
		return userList;
	}
	
	
	
	public boolean testBatchUpdate(List<User> userList){
		String sql = "insert into user (userName, passWord) VALUES (:userName, :passWord)";
		SqlParameterSource[] sqlArray = new SqlParameterSource[userList.size()];
		for(int i=0; i<userList.size(); i++){
			SqlParameterSource param = new BeanPropertySqlParameterSource(userList.get(i));
			sqlArray[i] = param;
		}
		simpleJdbcTemplate.batchUpdate(sql, sqlArray);
		
		
//		String sql = "insert into user (userName, passWord) VALUES (? ,?)";
//		List<Object[]> batchArgs = new ArrayList<Object[]>();
//		for(User user:userList){
//			batchArgs.add(new Object[]{user.getUserName(), user.getPassWord()});
//		}
//		simpleJdbcTemplate.batchUpdate(sql, batchArgs);
		
		
		//该方法比较老，不知道与新方法比较而言，哪个更好，先留下注释版本，待研究
//		final List<User> batchUser = userList;
//		simpleJdbcTemplate.getJdbcOperations().batchUpdate(sql, 
//				new BatchPreparedStatementSetter() {
//					
//					@Override
//					public void setValues(PreparedStatement ps, int index) throws SQLException {
//						User user = batchUser.get(index);
//						ps.setString(1, user.getUserName());
//						ps.setString(2, user.getPassWord());
//						//每2000条进行事物提交   
//	                    if (index%2000 == 0) {   
//	                        ps.executeBatch(); //执行prepareStatement对象中所有的sql语句   
//	                    }   
//					}
//					
//					@Override
//					public int getBatchSize() {
//						return batchUser.size();
//					}
//				}
//		);
		return true;
	}

}
